#!/usr/bin/env python
# -*- coding:utf-8 -*-

"""
    this is function description
"""
import json, datetime
import pandas as pd
import plotly.figure_factory as ff
import matplotlib.pyplot as plt

# import warnings
# warnings.filterwarnings('error')

SELF_GAP = 15  # 同机位安全间隔
NEIGHBOR_GAP = 5  # 相邻机位安全间隔
CURRENT_TIME = None
CloseGateNum = 0


class Flight:
    # 航班数据结构
    def __init__(self, ArriveNo=None, PlaneNo=None, MDL=None, ATime=None, DTime=None):
        self.arriveNo = ArriveNo  # 飞机进场序号
        self.planeNo = PlaneNo  # 机号
        self.mdl = MDL  # 机型
        self.atime = ATime  # 进港时间
        self.dtime = DTime  # 出港时间
        self.parkingGate = None  # 分配的停机位
        self.bridge = None  # 是否拥有廊桥是否近机位，1是0否


class Gate:
    # 停机位数据结构
    def __init__(self, GateNo, Brige):
        self.gateNo = GateNo  # 机位号
        self.brige = Brige  # 是否拥有廊桥是否近机位，1是0否
        self.available = 0  # 机位是否空闲，0空1占用
        self.AT = None  # 机位可操作时间（直到什么时候才允许飞机进出）,空闲时为
        self.CPlane = None  # 当前停放的飞机
        self.PPlane = None  # 停放的前一架飞机
        self.priority = 1  # 机位优先级，越大越高
        self.leftNeighbor = None  # 相邻左机位
        self.rightNeighbor = None  # 右机位


def print_objlist(objlist):
    # 打印对象列表
    for i in objlist:
        print(i.__dict__)


def initialization(File):
    # 初始化航班和机位数据
    with open(File, 'r', encoding='utf8') as load_f:
        load_dict = json.load(load_f)
        flight_list = load_dict['flight']
        gate_list = load_dict['gate']

        # 航班信息初始化，按入场时间排序
        flight_list = sorted(flight_list, key=lambda x: x['atime'], reverse=False)
        # print("flight_list: ", flight_list)
        new_flight_list = []
        ArriveNo = 1
        for f in flight_list:
            if f["atime"] is not None:
                if f["dtime"] == "":
                    # 没有给出离开时间，默认添加12小时
                    f["dtime"] = datetime.datetime.strptime(f["atime"], "%Y-%m-%d %H:%M:%S") + datetime.timedelta(
                        hours=12)
                    f["dtime"] = str(f["dtime"])
                flight = Flight(ArriveNo, f["planeno"], f["mdl"],
                                datetime.datetime.strptime(f["atime"], "%Y-%m-%d %H:%M:%S"),
                                datetime.datetime.strptime(f["dtime"], "%Y-%m-%d %H:%M:%S"))
                new_flight_list.append(flight)
                ArriveNo += 1
            else:
                print('航班数据错误：', f)
                continue
        # print_objlist(new_flight_list)
        print("航班初始化完成")

        # 机位初始化，按机位号排序
        new_gate_list = []
        new_gate_list_P3 = []
        new_gate_list_P2 = []
        new_gate_list_P1 = []
        # print("gate_list: ", gate_list)
        for g in gate_list:
            gate = Gate(int(g["ParkingGate"]), g["bridge"])
            pplane = Flight()
            pplane.atime = datetime.datetime(1, 1, 1, 0, 0, 0, 0)
            pplane.dtime = datetime.datetime(1, 1, 1, 0, 0, 0, 0)
            gate.PPlane = pplane
            new_gate_list.append(gate)

        # 机位优先级分类
        for g in new_gate_list:
            if g.gateNo in [16, 15, 11, 10, 9]:
                g.priority = 3
                new_gate_list_P3.append(g)
            elif g.gateNo in [8, 7, 5, 4, 3]:
                g.priority = 2
                new_gate_list_P2.append(g)
            else:
                new_gate_list_P1.append(g)
        new_gate_list = sorted(new_gate_list, key=lambda x: int(x.gateNo), reverse=False)

        for g in new_gate_list:
            # 添加机位相邻结点
            if g.gateNo > 1:
                g.leftNeighbor = new_gate_list[g.gateNo - 2]
            if g.gateNo < len(new_gate_list):
                g.rightNeighbor = new_gate_list[g.gateNo]

        new_gate_list = sorted(new_gate_list, key=lambda x: int(x.gateNo), reverse=False)
        # print_objlist(new_gate_list)
        print("机位初始化完成")

        return new_flight_list, new_gate_list, new_gate_list_P1, new_gate_list_P2, new_gate_list_P3


def gate_check(flight, CgateList, Gate_list):
    global CloseGateNum
    # 机位停放条件检查
    """
    :param flight: Flight对象
    :param CgateList: 当前遍历机位列表
    :param Gate_list: 所有机位列表
    :return:
    """
    for g in CgateList:
        # 若当前机位空闲
        if g.available == 0:
            if g.gateNo == 7 and flight.mdl != 'B737':
                # 7号机位仅允许停放B737
                continue

            # 同一机位至少间隔15分钟
            if (g.PPlane.dtime + datetime.timedelta(minutes=SELF_GAP)).__gt__(flight.atime):
                continue

            # 非两端机位
            if g.leftNeighbor != None and g.rightNeighbor != None:
                # 1.左右机位均空闲,A的dtime+15<C的atime,左右机位最快可使用时间<当前的atime
                if g.leftNeighbor.available == 0 and g.rightNeighbor.available == 0:
                    if (g.PPlane.dtime + datetime.timedelta(minutes=SELF_GAP)).__lt__(flight.atime) and \
                            g.leftNeighbor.AT.__lt__(flight.atime) and \
                            g.rightNeighbor.AT.__lt__(flight.atime):
                        # 安置航班
                        g.available = 1
                        g.CPlane = flight
                        g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                        if g.priority in [2, 3]:
                            CloseGateNum += 1
                        return True, g
                    else:
                        continue

                # 2.左右只有一个机位空闲,A的atime+5<C的atime and C的atime+5<A的dtime and (A的dtime+5>C的dtime or c的dtime+5<A的dtime)
                elif (g.leftNeighbor.available == 0 and g.rightNeighbor.available != 0) or (
                        g.rightNeighbor.available == 0 and g.leftNeighbor.available != 0):
                    # 左边占用
                    if g.leftNeighbor.available == 1:
                        if (g.leftNeighbor.CPlane.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                                flight.atime) and (
                                flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                            g.leftNeighbor.CPlane.dtime) and (
                                (g.leftNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                                    flight.dtime) or
                                g.leftNeighbor.CPlane.dtime.__gt__(
                                    flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP))
                        ):
                            # 安置航班
                            g.available = 1
                            g.CPlane = flight
                            g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                            if g.priority in [2, 3]:
                                CloseGateNum += 1
                            return True, g
                        else:
                            continue

                    # 右边占用
                    if g.rightNeighbor.available == 1:
                        if (g.rightNeighbor.CPlane.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                                flight.atime) and (
                                flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                            g.rightNeighbor.CPlane.dtime) and (
                                (g.rightNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                                    flight.dtime) or
                                g.rightNeighbor.CPlane.dtime.__gt__(
                                    flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP))
                        ):
                            # 安置航班
                            g.available = 1
                            g.CPlane = flight
                            g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                            if g.priority in [2, 3]:
                                CloseGateNum += 1
                            return True, g
                        else:
                            continue

                elif g.leftNeighbor.available != 0 and g.rightNeighbor.available != 0:
                    # 左右都不空闲

                    if (g.leftNeighbor.CPlane.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                            flight.atime) and (
                            flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                        g.leftNeighbor.CPlane.dtime) and (
                            g.leftNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                        flight.dtime) and (
                            g.rightNeighbor.CPlane.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                        flight.atime) and (
                            flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__lt__(
                        g.rightNeighbor.CPlane.dtime) and (
                            g.rightNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                        flight.dtime) and (
                            (g.leftNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                                flight.dtime) or
                            g.leftNeighbor.CPlane.dtime.__gt__(flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP))
                    ) and (
                            (g.rightNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                                flight.dtime) or
                            g.rightNeighbor.CPlane.dtime.__gt__(flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP))
                    ):
                        # 安置航班
                        g.available = 1
                        g.CPlane = flight
                        g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                        if g.priority in [2, 3]:
                            CloseGateNum += 1
                        return True, g
                    else:
                        continue

            # 最左边的机位
            if g.leftNeighbor == None:
                if g.rightNeighbor.available == 0:
                    if (g.PPlane.dtime + datetime.timedelta(minutes=15)).__lt__(flight.atime) and \
                            g.rightNeighbor.AT.__lt__(flight.atime):
                        # 安置航班
                        g.available = 1
                        g.CPlane = flight
                        g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                        if g.priority in [2, 3]:
                            CloseGateNum += 1
                        return True, g
                    else:
                        continue

                if g.rightNeighbor.available == 1:
                    if (g.rightNeighbor.CPlane.atime + datetime.timedelta(minutes=5)).__lt__(flight.atime) and (
                            flight.atime + datetime.timedelta(minutes=5)).__lt__(g.rightNeighbor.CPlane.dtime) and (
                            (g.rightNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                                flight.dtime) or
                            g.rightNeighbor.CPlane.dtime.__gt__(flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP))
                    ):
                        # 安置航班
                        g.available = 1
                        g.CPlane = flight
                        g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                        if g.priority in [2, 3]:
                            CloseGateNum += 1
                        return True, g
                    else:
                        continue

            # 最右边的机位
            if g.rightNeighbor == None:
                if g.leftNeighbor.available == 0:
                    if (g.PPlane.dtime + datetime.timedelta(minutes=15)).__lt__(flight.atime) and \
                            g.leftNeighbor.AT.__lt__(flight.atime):
                        # 安置航班
                        g.available = 1
                        g.CPlane = flight
                        g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                        if g.priority in [2, 3]:
                            CloseGateNum += 1
                        return True, g
                    else:
                        continue

                if g.leftNeighbor.available == 1:
                    if (g.leftNeighbor.CPlane.atime + datetime.timedelta(minutes=5)).__lt__(flight.atime) and (
                            flight.atime + datetime.timedelta(minutes=5)).__lt__(g.leftNeighbor.CPlane.dtime) and (
                            (g.leftNeighbor.CPlane.dtime + datetime.timedelta(minutes=NEIGHBOR_GAP)).__gt__(
                                flight.dtime) or
                            g.leftNeighbor.CPlane.dtime.__gt__(flight.atime + datetime.timedelta(minutes=NEIGHBOR_GAP))
                    ):
                        # 安置航班
                        g.available = 1
                        g.CPlane = flight
                        g.AT = flight.dtime + datetime.timedelta(minutes=SELF_GAP)
                        if g.priority in [2, 3]:
                            CloseGateNum += 1
                        return True, g
                    else:
                        continue

        else:
            continue

    return False, g


# 机位状态刷新
def FlightArrive(Flight, Gate_list):
    global CURRENT_TIME
    CURRENT_TIME = Flight.atime
    print("当前时间：", CURRENT_TIME)
    # 更新机位状态
    for G in Gate_list:
        # 若当前时间超过飞机离开时间
        if G.available == 1 and (CURRENT_TIME.__gt__(G.CPlane.dtime)):
            G.available = 0  # 释放机位
            G.PPlane = G.CPlane  # 停放的前一架飞机
            G.CPlane = None


if __name__ == '__main__':
    File = r'./flight.json'
    init = initialization(File)
    flight_list = init[0]
    gate_list = init[1]
    gate_list_P1 = init[2]
    gate_list_P2 = init[3]
    gate_list_P3 = init[4]
    print_objlist(flight_list)
    print_objlist(gate_list)

    d1 = []
    # 贪心策略
    for flight in flight_list:
        # 刷新机位
        FlightArrive(flight, gate_list)
        # 优先选择第一批近机位
        res = gate_check(flight, gate_list_P3, gate_list)

        if res[0] is True:
            d1.append([flight.planeNo, res[1].gateNo, flight.__dict__['atime'], flight.__dict__['dtime']])
            print("当前降落航班： ", flight.planeNo, " 停靠机位：", res[1].gateNo)
            print("航班信息：", flight.__dict__, " 机位信息：", res[1].__dict__)
            continue
        # 其次选择第二批近机位
        else:
            res = gate_check(flight, gate_list_P2, gate_list)
            if res[0] is True:
                d1.append([flight.planeNo, res[1].gateNo, flight.__dict__['atime'], flight.__dict__['dtime']])
                print("当前降落航班： ", flight.planeNo, " 停靠机位：", res[1].gateNo)
                print("航班信息：", flight.__dict__, " 机位信息：", res[1].__dict__)
                continue
                # 最后选择远机位
            else:
                res = gate_check(flight, gate_list_P1, gate_list)
                d1.append([flight.planeNo, res[1].gateNo, flight.__dict__['atime'], flight.__dict__['dtime']])
                if res[0] is True:
                    print("当前降落航班： ", flight.planeNo, " 停靠机位：", res[1].gateNo)
                    print("航班信息：", flight.__dict__, " 机位信息：", res[1].__dict__)
                else:
                    print("机位繁忙需等待")
                    break

    print("CloseGateNum：", CloseGateNum)
    print("当前近机位利用率：", float(CloseGateNum / len(flight_list)) * 100, "%")

    df = pd.DataFrame(d1, columns=['planeNo', 'Task', 'Start', 'Finish'])
    df = df.sort_values(by="Task", ascending=False)

    # colors = {'B737': 'rgb(220, 0, 0)','B736': (1, 0.9, 0.16),'A319':  (1, 0.9, 0.16),'A321':'rgb(0, 255, 100)'}
    fig = ff.create_gantt(df, showgrid_x=True, show_colorbar=True, showgrid_y=True, group_tasks=True)
    fig.show()
